package wsi.function;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.rygh.core.model.common.MapList;
import org.rygh.core.model.common.MapSet;
import org.rygh.core.util.UIo;
import org.rygh.semantic.model.rdf.Predicate;

import wsi.script.AppConfig;
import wsi.ws.WebServices;

public class FunctionController {
	private static FunctionController shared;

	public final static FunctionController shared() {
		if (shared == null) {
			shared = new FunctionController();
			shared.load();
		}

		return shared;
	}

	private final MapList<String, Function> functionByServiceMap = new MapList<>();
	private final MapList<String, Function> functionByInputTypeMap = new MapList<>();
	private final Map<Function, String> functionByInputTypeInverseMap = new HashMap<>();

	private final List<Function> allFunctions = new ArrayList<>();

	private FunctionController() {
	}

	public void copyTo(File dir) {
		System.out.println("copyTo " + allFunctions.size());

		for (Function function : allFunctions) {
			File serv = new File(dir, function.getService());

			if (!serv.exists())
				serv.mkdir();

			UIo.saveFile(
					new File(serv, String.format("%s.txt", function.getName())),
					function.toMachineString());

			UIo.saveFile(
					new File(serv, String.format("%s.xml", function.getName())),
					function.toXmlString());
		}
	}

	public List<Function> getAllFunctions() {
		return allFunctions;
	}

	public Function getFunction(String service, String name) {
		if (!functionByServiceMap.containsKey(service))
			return null;

		for (Function f : functionByServiceMap.get(service))
			if (f.getName().equals(name))
				return f;

		return null;
	}

	public MapList<String, Function> getFunctionByInputTypeMap() {
		return functionByInputTypeMap;
	}

	public List<Function> getFunctions(String service) {
		return functionByServiceMap.get(service);
	}

	public String getInputType(Function function) {
		return functionByInputTypeInverseMap.get(function);
	}

	public void load() {
		functionByServiceMap.clear();
		allFunctions.clear();

		File fdir = AppConfig.shared().getFunctionsFolder();

		for (WebServices service : WebServices.values()) {
			File dir = new File(fdir, service.name());

			if (!dir.exists() || !dir.isDirectory())
				continue;

			File[] files = dir.listFiles(new FilenameFilter() {

				@Override
				public boolean accept(File file, String name) {
					return name.startsWith("get") && name.endsWith(".txt");
				}
			});

			for (File f : files) {
				// System.out.println("loading " + service.name() + " "
				// + f.getName());
				load(f, service.name());
			}
		}
	}

	private void load(File file, String service) {
		String content = UIo.readFile(file);
		String[] lines = content.split("\r\n");

		Function function = new Function(service,
				UIo.getFileNameWithoutExtension(file.getName()));

		for (String line : lines) {
			if (line == null || line.trim().isEmpty() || line.startsWith("//"))
				continue;

			int pos = line.indexOf(':');
			assert pos != -1 : line + ".";

			String key, value;

			key = line.substring(0, pos);
			value = line.substring(pos + 1);

			function.update(key, value);
		}

		String fname = UIo.getFileNameWithoutExtension(file.getName());

		assert fname.equals(function.getName()) : service + fname + ","
				+ function.getName();

		functionByServiceMap.put(service, function);
		allFunctions.add(function);
	}

	public void loadByInputType() {
		functionByInputTypeMap.clear();

		Map<String, String> map = new HashMap<>();
		map.put("author_1", "person");
		map.put("singer_1", "person");
		map.put("country_1", "country");
		map.put("country_2", "country");
		map.put("actor_1", null);

		AppConfig ac = AppConfig.shared();
		String id, nid;

		for (Function f : allFunctions) {
			RuntimeInfo config = ac.getConfig(f);

			if (config == null)
				continue;

			id = config.getQueryId();

			if (id == null)
				id = "identifier";

			if (map.containsKey(id)) {
				nid = map.get(id);

				if (nid == null)
					continue;
			} else
				nid = id;

			if (nid.indexOf('_') != -1)
				nid = nid.substring(0, nid.indexOf('_'));

			functionByInputTypeInverseMap.put(f, nid);
			functionByInputTypeMap.put(nid, f);
		}

		processIdPreds();
	}

	private void processIdPreds() {
		AppConfig ac = AppConfig.shared();
		RuntimeInfo config;

		MapSet<Function, Predicate> predMap = new MapSet<>();
		Function prov_func;
		Predicate id_pred;

		for (Function f : allFunctions) {
			if (f.isDirect())
				continue;

			config = ac.getConfig(f);

			if (config == null)
				continue;

			prov_func = config.getProviderFunction();
			id_pred = config.getIdPredicate();

			predMap.put(prov_func, id_pred);
		}

		for (Function f : allFunctions) {
			if (!f.isDirect() || !predMap.containsKey(f))
				continue;

			f.setIdPredicates(predMap.get(f));
		}
	}

	@Override
	public String toString() {
		StringBuilder sbuilder = new StringBuilder();

		for (WebServices service : WebServices.values()) {
			List<Function> list = functionByServiceMap.get(service.name());

			sbuilder.append(String.format("%s (%d functions)\n", service,
					list.size()));

			for (Function f : list) {
				sbuilder.append(String.format("\nFunction %s\n", f.getName()));
				sbuilder.append(f);
			}
		}

		return sbuilder.toString();
	}
}
